package pl.enigmatic.parser.xml;

import org.w3c.dom.Node;

import pl.enigmatic.math.AliasedMap;
import pl.enigmatic.util.Strings;


public abstract class BasicPropertyParser<A extends BasicPropertyParser.Accessor> implements XMLParserExtension {

	public static String getPropertyName(final Node node, final PropertiesNamesMap namesMap) {
		final String property = namesMap.getPropertyName(node.getLocalName());
		if (node.getPrefix() == null && !property.contains(".") && Strings.lowerStarted(property)) { return property; }
		return null;
	}

	protected static abstract class Accessor implements PropertySetter {

		private final String propertyName;
		private final Class<?> type;
		private final Parser<?> parser;

		protected Accessor(final String propertyName, final Class<?> type, final Parser<?> parser) {
			this.propertyName = propertyName;
			this.type = type;
			this.parser = parser;
		}

		@Override
		public Parser<?> getParser() {
			return parser;
		}

		@Override
		public String getPropertyName() {
			return propertyName;
		}

		public Class<?> getType() {
			return type;
		}
	}

	/** public methods for setting property values */
	protected final AliasedMap<String, A> accessors = new AliasedMap<String, A>();
	protected final Parser<?> owner;
	protected final boolean parseClassName;

	protected BasicPropertyParser(final Parser<?> owner, final boolean parseClassName) {
		this.owner = owner;
		this.parseClassName = parseClassName;
	}

	@Override
	public NodeNameInfo parseNodeName(final Node node, final ParsingInfo<?> info) {
		final String property = getPropertyName(node, owner);
		if (accessors.containsKey(property)) {
			if (parseClassName) {
				final String clazz = NodeNameInfo.getFullClassName(node, info.getStaticInfo());
				if (clazz != null) { return new NodeNameInfo(property, clazz, Parser.createParser(clazz)); }
			}
			final Accessor s = accessors.get(property);
			return new NodeNameInfo(property, s.getType(), s.getParser());
		}
		return null;
	}

	@Override
	public boolean setParsedProperty(final Object target, final ParsingInfo<?> info, final Object value) {
		if (accessors.containsKey(info.getNodeInfo().getProperty())) {
			return accessors.get(info.getNodeInfo().getProperty()).access(target, value, info);
		} else {
			return false;
		}
	}

	@Override
	public PropertySetter accessor(final String property) {
		return accessors.get(property);
	}
}
